#include <gtest/gtest.h>

#include "MemoryDBTest.h"
#include "helper/unittest/Tracer.h"

#include "helper/sqlite/IDB.h"
#include "helper/sqlite/Statement.h"

class DBQueryTest : public test::MemoryDBTest {
protected:
  void insert(const std::string & one, int two){
    std::error_code ec;
    auto && stmt = db().createStatement("insert into tab1 values(?, ?)", ec);
    ASSERT_TRUE(stmt && !ec);

    ec = stmt->textBind(1, one);
    ASSERT_EQ(std::error_code(), ec);
    ec = stmt->intBind(2, two);
    ASSERT_EQ(std::error_code(), ec);

    auto && r = stmt->execute();
    ASSERT_TRUE(r && 1 == r.affectRows());
  }
  void SetUp() override {
    MemoryDBTest::SetUp();
    insert("query_test1", 1);
    insert("query_test2", 2);
    insert("query_test3", 3);
  }

  void TearDown() override {
    std::error_code ec;
    auto && stmt = db().createStatement("delete from tab1 where one like 'query_test%'", ec);
    ASSERT_TRUE(stmt && !ec);

    auto && r = stmt->execute();
    ASSERT_TRUE(r && r.affectRows() == 3);

    MemoryDBTest::TearDown();
  }
};


TEST_F(DBQueryTest, dql_fetch_scalar){
  std::error_code ec;
    auto && stmt = db().createStatement("select count(1) from tab1;", ec);
    ASSERT_TRUE(stmt && !ec);

    auto && rs = stmt->query();
    ASSERT_TRUE(rs && rs.isOk()); //ROW

    auto && value = rs.values(0);
    ASSERT_TRUE(value);
    ASSERT_FALSE(value->isNull());
    ASSERT_TRUE (value->isInteger());
    ASSERT_FALSE(value->isReal());
    ASSERT_FALSE(value->isString());
    ASSERT_FALSE(value->isBinary());

    ASSERT_EQ(3,   value->integer());
    ASSERT_EQ(3.0, value->real());
    ASSERT_EQ("3", value->binary());
    ASSERT_EQ("3", value->string());

    ASSERT_TRUE(rs);
}

TEST_F(DBQueryTest, dql_fetch_table){

  std::error_code ec;
  auto && stmt = db().createStatement("select * from tab1 order by one desc;", ec);
  ASSERT_TRUE(stmt && !ec);

  EXPECT.reset()
    .operation("query_test3").operation("3")
    .operation("query_test2").operation("2")
    .operation("query_test1").operation("1")
    ;
  TRACER.reset();
  for(auto && rs = stmt->query(); rs; rs.next()){

    ASSERT_EQ(2, rs.columnCount());
    TRACER.operation(rs.values(0)->string())
        .operation(rs.values(1)->string())
        ;
  }
  ASSERT_EQ(EXPECT.toString(), TRACER.toString());
}

TEST_F(DBQueryTest, dql_fetch_table_with_prepare){
  std::error_code ec;
  auto && stmt = db().createStatement("select * from tab1 where one like ?", ec);
  ASSERT_TRUE(stmt && !ec);

  //prepareInput 
  std::string text("query_test%");
  stmt->prepareInput([&text](auto & stmt) {
    return stmt.textBind(1, text);
  });
  
  EXPECT.reset()
    .operation("query_test1").operation("1")
    .operation("query_test2").operation("2")
    .operation("query_test3").operation("3")
    ;
  TRACER.reset();
  // fetch query  result 
  auto && rs = stmt->query();
  while(rs){

    ASSERT_EQ(2, rs.columnCount());
    TRACER
      .operation(rs.values(0)->string())
      .operation(rs.values(1)->string())
          ;

    rs.next();
  }
  ASSERT_EQ(EXPECT, TRACER);
}



#include "helper/sqlite/QueryResult.h"
using QueryResult = helper::sqlite::QueryResult;

TEST_F(DBQueryTest, moveQueryResult){
  std::error_code ec;
  auto && stmt = db().createStatement("select * from tab1;", ec);
  ASSERT_TRUE(stmt && !ec);

  QueryResult && r = stmt->query();
  ASSERT_TRUE(r);
  
  QueryResult * p_r = new QueryResult(std::move(r));
  ASSERT_TRUE(!r && *p_r);

  QueryResult new_r = std::move(*p_r);
  ASSERT_TRUE(!*p_r && new_r);
}
